package trb.complexshield.benchmarking;

import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import javax.media.j3d.IndexedGeometryArray;
import javax.media.j3d.Shape3D;
import no.hrp.hvrc.property.editor.AppearanceProperties;
import no.hrp.hvrc.property.editor.SetDirectly;
import no.hrp.hvrc.utils.j3d.GeometryExtractor;
import no.hrp.hvrc.utils.j3d.Mat4;
import no.hrp.hvrc.utils.j3d.ObjectViewer;
import no.hrp.hvrc.utils.j3d.SGUtils;
import no.hrp.hvrc.utils.j3d.Vec3;
import no.hrp.hvrc.utils.j3d.geometry.Extrusion;
import no.hrp.hvrc.utils.j3d.geometry.IndexedMesh;
import no.hrp.hvrc.utils.j3d.geometry.IndexedTrianglesGeometry;
import trb.complexshield.ComplexShield;
import trb.complexshield.bool.model.BoolCylinder;
import trb.complexshield.bool.model.BoolSubtract;
import trb.complexshield.closedmesh.BvhCollisionMesh;
import trb.complexshield.closedmesh.ClosedMesh;
import trb.complexshield.voxel.VoxelFromBool;

/**
 * A tube going along the y axis.
 */
public class BenchmarkTube {

    public static void main(String[] args) {
        IndexedMesh indexedMesh = createIndexedMesh(new BenchmarkTube(0.5f, 1f, 0f, 2f, new Mat4()), 15);
        IndexedGeometryArray geom = new IndexedTrianglesGeometry(indexedMesh).geom;
        AppearanceProperties appearanceProperties = new AppearanceProperties(null, new SetDirectly(), null);
        appearanceProperties.diffuse.set(Color.BLUE);
        ObjectViewer.view(SGUtils.wrapInBrancGroup(new Shape3D(geom, appearanceProperties.createNewAppearance())));
    }

    private final float innerRadius;
    private final float outerRadius;
    private final float miny;
    private final float maxy;
    private final Mat4 mat4;

    public BenchmarkTube(float innerRadius, float outerRadius, float miny, float maxy, Mat4 mat4) {
        this.innerRadius = innerRadius;
        this.outerRadius = outerRadius;
        this.miny = miny;
        this.maxy = maxy;
        this.mat4 = mat4;
    }

    public static ComplexShield createBool(BenchmarkTube tube) {
        return BenchmarkTub.createComplexShieldFromBoolModel(createBoolModel(tube));
    }

    public static ComplexShield createVoxel(BenchmarkTube tube, int dim) {
        return VoxelFromBool.createVoxel(createBoolModel(tube), dim);
    }

    public static Object createBoolModel(BenchmarkTube tube) {
        BoolCylinder outer = new BoolCylinder(tube.outerRadius, tube.maxy - tube.miny, tube.mat4);
        BoolCylinder inner = new BoolCylinder(tube.innerRadius, tube.maxy - tube.miny, tube.mat4);
        return new BoolSubtract(outer, inner);
    }

    public static ClosedMesh createClosedMesh(BenchmarkTube tube, int divisions, AtomicInteger triangleCount) {
        IndexedMesh indexedMesh = createIndexedMesh(tube, divisions);
        triangleCount.set(indexedMesh.indices.length / 3);
        return new ClosedMesh(indexedMesh.createCoordinates(), indexedMesh.createIndices(), tube.mat4);
    }

    public static BvhCollisionMesh createBvhMesh(BenchmarkTube tube, int divisions, AtomicInteger triangleCount) {
        try {
            IndexedMesh indexedMesh = createIndexedMesh(tube, divisions);
            triangleCount.set(indexedMesh.indices.length / 3);
            IndexedTrianglesGeometry indexedTrianglesGeometry = new IndexedTrianglesGeometry(indexedMesh);
            GeometryExtractor geometryExtractor = GeometryExtractor.extractInWorldSpace(new Shape3D(indexedTrianglesGeometry.geom));
            return new BvhCollisionMesh(geometryExtractor.getCoords(), geometryExtractor.getIndices(), new Mat4());
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new RuntimeException("Failed to load tub model");
        }
    }

    public static IndexedMesh createIndexedMesh(BenchmarkTube cylinder, int divisions) {
        List<Vec3> line = new ArrayList();
        line.add(new Vec3(cylinder.innerRadius, cylinder.miny, 0));
        line.add(new Vec3(cylinder.innerRadius, cylinder.maxy, 0));
        line.add(new Vec3(cylinder.outerRadius, cylinder.maxy, 0));
        line.add(new Vec3(cylinder.outerRadius, cylinder.miny, 0));

        List<Mat4> transformations = new ArrayList();
        for (int i=0; i<divisions; i++) {
            transformations.add(new Mat4().rotateEuler(0, (float) (Math.PI * 2 * i / divisions), 0f));
        }

        return Extrusion.createMesh(line, transformations, true, true);
    }
}
